#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
//#include <windows.h> //WORD, DWORD, LONG

//constants taken from CS4953 bitmap c++
#define BI_RGB        0L        // (no compression)
#define BI_RLE8       1L        // (run-length encoding, 8 bits)
#define BI_RLE4       2L        // (run-length encoding, 4 bits)
#define BI_BITFIELDS  3L
#define BI_JPEG       4L
#define BI_PNG        5L

//windows.h not available, had to make new typdefs
typedef unsigned int DWORD;
typedef unsigned short WORD;
typedef int LONG;
typedef unsigned char BYTE;

//###the following structure information is taken from wingdi.h###
//Bitmap File Header
typedef struct tagBITMAPFILEHEADER{ //14 bytes
   WORD    bfType;      // ASCII "BM"
   DWORD   bfSize;      // total length of bitmap file
   WORD    bfReserved1; // reserved
   WORD    bfReserved2; // reserved
   DWORD   bfOffBits;   // offset to start of actual pixel data
} BITMAPFILEHEADER;
//DIB Header
typedef struct tagBITMAPINFOHEADER{ //40 bytes
   DWORD   biSize;              // size of BITMAPINFOHEADER
   LONG    biWidth;             // width in pixels
   LONG    biHeight;            // height in pixels
   WORD    biPlanes;            // always 1
   WORD    biBitCount;          // color bits per pixel
   DWORD   biCompression;       // BI_RGB, BI_RLE8, BI_RLE4
   DWORD   biSizeImage;         // total bytes in image
   LONG    biXPelsPerMeter;     // 0, or optional horizontal resolution
   LONG    biYPelsPerMeter;     // 0, or optional vertical resolution
   DWORD   biClrUsed;           // colors actually used (normally zero, can be lower than biBitCount)
   DWORD   biClrImportant;      // important colors actualy used (normally zero)
} BITMAPINFOHEADER;
typedef struct tagRGBQUAD{
   BYTE    rgbBlue;
   BYTE    rgbGreen;
   BYTE    rgbRed;
   BYTE    rgbReserved;
} RGBQUAD;

static void displayFileInfo(char *pFileName, BITMAPFILEHEADER *pFileHdr, BITMAPINFOHEADER *pFileInfo, RGBQUAD *pColorTable, unsigned char *pData);
static void readBMP(char *file, int *fileSize, unsigned char **image);
static int writeBMP(unsigned char *image, int fileSize, char *fileName);

int main(int argc, char *argv[]){
   int fileSz;
   unsigned char *image, *data;
   BITMAPFILEHEADER *bmpHeader;
   BITMAPINFOHEADER *bmpInfoHeader;
   
   RGBQUAD *bmpColorTable;

   //as we increase complexity, make sure to error check argument list passed.
   //IE, update "usage:"to
   if(argc == 1){
      fprintf(stderr,"Usage: hideex <*.bmp>\n");
      exit(1);
   }

   //read in source *.bmp
   readBMP(argv[1], &fileSz, &image);

   //begin reading in bitmap headers here
   bmpHeader = (BITMAPFILEHEADER *) image;
   bmpInfoHeader = (BITMAPINFOHEADER *) (image + sizeof(BITMAPFILEHEADER));
   bmpColorTable = (RGBQUAD *) (image + sizeof(BITMAPFILEHEADER) + 
                                bmpInfoHeader->biSize);
   data = image + bmpHeader->bfOffBits;

   displayFileInfo(argv[1], bmpHeader, bmpInfoHeader, bmpColorTable, data);

   //write out data
   writeBMP(image, fileSz, argv[1]);

   return 0;
}

static void displayFileInfo(char *pFileName,

					 BITMAPFILEHEADER *pFileHdr, 

					 BITMAPINFOHEADER *pFileInfo,

					 RGBQUAD *pColorTable,

					 unsigned char *pData)

{
   char *pp = (char *) &(pFileHdr->bfType);
   int numColors, i;
   RGBQUAD *pCT = pColorTable;

   printf("\nFile Info for %s: \n\n", pFileName);



   printf("File Header Info:\n");

   printf("File Type: %c%c\nFile Size:%x\nData Offset:%d\n\n", 

	*pp, *(pp + 1), pFileHdr->bfSize, pFileHdr->bfOffBits);


   numColors = 1 << (pFileInfo->biBitCount);
   printf("Bit Map Image Info:\n\nSize:%d\nWidth:%d\nHeight:%d\nPlanes:%d\n"

	"Bits/Pixel:%d ==> %d colors\n"

	"Compression:%d\nImage Size:%d\nRes X:%d\nRes Y:%d\nColors:%d\nImportant Colors:%d\n\n",

	pFileInfo->biSize,

	pFileInfo->biWidth, 

	pFileInfo->biHeight, 

	pFileInfo->biPlanes, 

	pFileInfo->biBitCount, numColors,

	pFileInfo->biCompression, 

	pFileInfo->biSizeImage, 

	pFileInfo->biXPelsPerMeter,

	pFileInfo->biYPelsPerMeter,

	pFileInfo->biClrUsed,

	pFileInfo->biClrImportant);
   /*if(pFileInfo->biBitCount > 16)
	return;
   printf("Color Table:\n\n");



   for(i = 0; i < numColors; i++)

   {

	printf("R:%02x   G:%02x   B:%02x\n", pCT->rgbRed, pCT->rgbGreen, pCT->rgbBlue);

	pCT++;

   }
   //*/
}
static int writeBMP(unsigned char *image, int fileSize, char *fileName){
   FILE *outfile;
   char fileBuffer[256];
   int offset, bytesWritten;

   offset = strlen(fileName)-4;
   strncpy(fileBuffer, fileName, offset);
   fileBuffer[offset] = '\0';
   strcat(fileBuffer, ".hx.bmp");
   if((outfile = fopen(fileBuffer, "w+"))==NULL){
      fprintf(stderr,"error writing to %s\n",fileBuffer);
      perror(NULL);
      exit(1);
   }
   if((bytesWritten = fwrite(image, sizeof(unsigned char), fileSize, outfile)) != fileSize){
      fprintf(stderr, "incorrect write to file. bytes to write: %d, bytes written: %d\n", fileSize, bytesWritten);
      perror("incorrect write to file\n");
      exit(1);
   }
   fclose(outfile);

   return 0;
}

static void readBMP(char *file, int *fileSize, unsigned char **image){
   FILE *infile;
   int offset;

   //check if *.bmp
   offset = strlen(file)-3;
   if(strcmp(file+offset,"bmp") != 0){
      fprintf(stderr,"%s is not of *.bmp\n",file);
      exit(1); //is it ok to exit from a helper function?
   }

   if((infile = fopen(file,"r")) == NULL){
      fprintf(stderr,"error opening file %s\n",file);
      perror(NULL);
      exit(1);
   }

   fseek(infile,0,SEEK_END);
   *fileSize = ftell(infile);
   fseek(infile,0,SEEK_SET);

   if((*image = (unsigned char *) malloc(*fileSize)) == NULL){
      perror("malloc error");
      exit(1);
   }
   fread(*image, sizeof(unsigned char), *fileSize, infile);
   fclose(infile);
}

/*
// TODO: assuming a header is made for each pixel
//       assuming binaryout is already opened and header is written in it
// pixel is the first pixel of the cover image
// data is the data to be hidden (image or executable)
// binaryout is the stego file to be outputted
static void hidedata(RGBQUAD *pixel, FILE *data, FILE *binaryout) {
   char *buf;
   int bytebuf, error, numbits, hidden, indicator, n, p;
   int r, g, b, res;
   srand(key); // seed rand to key
   p = 0; // current pixel of total
   bytebuf = 0; // buffer of read bytes from data
   error = 0; // condition for EoF
   numbits = 0; // number of bits in bytebuf
   do {
      if(bytebuf < 1) { //if bytebuf is less than a byte, read in next byte
         if((error = read(data, buf, 1)) != -1) {
            bytebuf = bytebuf << 8 + *buf;
            numbits += 8;
         }
      }
      r = pixel->rgbRed;
      g = pixel->rgbGreen;
      b = pixel->rgbBlue;
      reserve = pixel->rgbReserved;
      // choose indicator channel
      switch(rand() % 3) {
         case 0: // RED
            if(g >= b) {
               // write RED + indicator
               indicator = (r >> 2) << 2 + 2; // replace the 2 LSB with '10' (left)
               write(binaryout, indicator, 1);
               // write GREEN + hidden
               n = 4; //TODO Decide N no. of bits to hide
               hidden = bytebuf; // get the next n bits from bytebuf
               if(numbits > n)
                  hidden = bytebuf >> (numbits-n);
               replace = (g >> (8-n)) << (8-n) + hidden;
               write(binaryout, replace, 1); // hide n bytes of data
               // write BLUE
               write(binaryout, b, 1); // unchanged
               // write RESERVED
               write(brinaryout, reserve, 1); // unchanged
               bytebuf = (bytebuf << n) >> n;
            }
            else {
               // write RED + indicator
               indicator = (r >> 2) << 2 + 1; // replace the 2 LSB with '01' (right)
               write(binaryout, indicator, 1);
               // write GREEN
               write(binaryout, g, 1); // unchanged
               // write BLUE + hidden
               n = 4; //TODO Decide N no. of bits to hide
               hidden = bytebuf; // get the next n bits from bytebuf
               if(numbits > n)
                  bytebuf = (bytebuf << 8-n);
               replace = (b >> (8-n)) << (8-n) + hidden;
               write(binaryout, replace, 1); // hide n bytes of data
               // write RESERVED
               write(brinaryout, reserve, 1); // unchanged
               bytebuf = (bytebuf << n) >> n;
            }
            break;
         case 1: // GREEN
            if(r >= b) {
               // write RED + hidden
               n = 4; //TODO Decide N no. of bits to hide
               hidden = bytebuf; // get the next n bits from bytebuf
               if(numbits > n)
                  bytebuf = (bytebuf << 8-n);
               replace = (r >> (8-n)) << (8-n) + hidden;
               write(binaryout, replace, 1); // hide n bytes of data
               // write GREEN + indicator
               indicator = (g >> 2) << 2 + 2; // replace the 2 LSB with '10' (left)
               write(binaryout, indicator, 1);
               // write BLUE
               write(binaryout, b, 1); // unchanged
               // write RESERVED
               write(brinaryout, reserve, 1); // unchanged
               bytebuf = (bytebuf << n) >> n;
            }
            else {
               // write RED
               write(binaryout, r, 1); // unchanged
               // write GREEN + indicator
               indicator = (r >> 2) << 2 + 1; // replace the 2 LSB with '01' (right)
               write(binaryout, indicator, 1);
               // write BLUE + hidden
               n = 4; //TODO Decide N no. of bits to hide
               hidden = bytebuf; // get the next n bits from bytebuf
               if(numbits > n)
                  bytebuf = (bytebuf << 8-n);
               replace = (b >> (8-n)) << (8-n) + hidden;
               write(binaryout, replace, 1); // hide n bytes of data
               // write RESERVED
               write(brinaryout, reserve, 1); // unchanged
               bytebuf = (bytebuf << n) >> n;
            }
            break;
         case 2: // BLUE
            if(r >= g) {
               // write RED + hidden
               n = 4; //TODO Decide N no. of bits to hide
               hidden = bytebuf; // get the next n bits from bytebuf
               if(numbits > n)
                  bytebuf = (bytebuf << 8-n);
               replace = (r >> (8-n)) << (8-n) + hidden;
               write(binaryout, replace, 1); // hide n bytes of data
               // write GREEN
               write(binaryout, g, 1); // unchanged
               // write BLUE + indicator
               indicator = (b >> 2) << 2 + 2; // replace the 2 LSB with '10' (left)
               write(binaryout, indicator, 1);
               // write RESERVED
               write(brinaryout, reserve, 1); // unchanged
               bytebuf = (bytebuf << n) >> n;
            }
            else {
               // write RED
               write(binaryout, r, 1); // unchanged
               // write GREEN + hidden
               n = 4; //TODO Decide N no. of bits to hide
               hidden = bytebuf; // get the next n bits from bytebuf
               if(numbits > n)
                  bytebuf = (bytebuf << 8-n);
               replace = (g >> (8-n)) << (8-n) + hidden;
               write(binaryout, replace, 1); // hide n bytes of data
               // write BLUE + indicator
               indicator = (g >> 2) << 2 + 1; // replace the 2 LSB with '01' (right)
               write(binaryout, indicator, 1);
               // write RESERVED
               write(brinaryout, reserve, 1); // unchanged
               bytebuf = (bytebuf << n) >> n;
            }
      } // end switch
      pixel++;
      p++;
   } // end while
   while(p < numPixels && numbits > 0)
} // end hide data
*/
